package org.ricks.dispatch;

import org.ricks.common.Assert;
import org.ricks.common.Context;
import org.ricks.common.Tuple;
import org.ricks.ioc.*;
import org.ricks.ioc.bean.factory.AbstractBeanFactory;
import org.ricks.log.Logger;
import org.ricks.net.ActionMethod;

import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * @author chenwei
 * @Description:指令调用管理器
 * 类加载的问题，导致自定义加载器
 * 方案一：
 *     static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
 *     Class clazz = JLA.defineClass(DispatchManager.class.getClassLoader(),"org.demon.dispatch.Dispatcher",data,null,null);
 * 方案二：
 *            Method mdDefineClass = ClassLoader.class
 *                     .getDeclaredMethod("defineClass",String.class,byte[].class,int.class,int.class);
 *             mdDefineClass.setAccessible(true);
 *             String className = "org.demon.dispatch.Dispatcher";
 *             Class clazz = (Class)mdDefineClass.invoke(DispatchManager.class.getClassLoader(),new Object[]{
 *                     className,data,0,data.length
 *             });
 *
 *  以上方案都不优雅，需要调用其它module 而jdk9之后 module之间互相调用 需要在启动jvm加上访问参数
 *
 *
 *  使用了 openJdk  JavaLangAccess ，就必须在启动参数加上 --add-opens java.base/jdk.internal.access=ALL-UNNAMED
 *  破坏了 Jdk9 module 之间不能调用的机制
 *
 *  reflectasm 实际上也是使用ASM生成了 switch 代码，进行直接调用。所以reflectasm的性能才会比jdk反射性能强
 *  2023-1-4 昨天晚上压测了几种调用方案。我自己写的这一套asm生成switch调用 和 直接调用相差无几。reflectasm还是和直接调用有小的差距
 *  现在问题可以直接使用我这一套asm生成switch机制，但是要去掉module访问的权限
 *
 *  方案三： 我他喵的就知道，会有方法可以直接 load class 。妈的 终于找到方案了。。MethodHandles.Lookup lookup = MethodHandles.lookup()
 *  那就直接使用我的方法，Asm 动态生成 class字节流 不生成class文件，直接load jvm 进而使用
 * @date 2021/3/1917:47
 */
public class DispatchManager {

    private static List<Tuple> tupleList = new ArrayList<>();
    private static final MethodHandles.Lookup lookup = MethodHandles.lookup();

    static {
        AbstractBeanFactory beanFactory = AppContext.getBeanFactory();
        Assert.notNull(beanFactory);
        List<Object> beans = beanFactory.getBeansForAnnotation(Bean.class);
        for (Object obj : beans) {
            Method[] methods = obj.getClass().getMethods();
            for (Method method : methods) {
                ActionMethod messageHandler = method.getAnnotation(ActionMethod.class);
                if (messageHandler == null) continue;
                Class<?>[] parameterClazzes = method.getParameterTypes();
                if (parameterClazzes.length != 1) throw new IllegalArgumentException("消息处理方法的参数不正确，参数必须是一个。");
                Class<?> commandClass = parameterClazzes[0];
                if (!Context.class.isAssignableFrom(commandClass)) continue;
                tupleList.add(new Tuple(messageHandler.messageId(),getClassName(obj).replace("/","."),method.getName()));
            }
        }

    }

    public static void init() {
        try {
//            String dispatchName = "org.ricks.dispatch.Dispatcher";
            byte[] data = DispatcherDump.dump(tupleList);
            lookup.defineClass(data);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static String getClassName(Object obj) {
        String beanName = obj.getClass().getName();
        return beanName.replace(".","/");
    }
}
